package com.cqcomponents.tags;

import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.jsp.JspException;

import org.apache.sling.api.resource.Resource;

import com.cqcomponents.api.components.IComponent;
import com.cqcomponents.api.exceptions.MandatoryComponentException;
import com.cqcomponents.cache.ComponentStatistics;
import com.cqcomponents.utils.ComponentUtils;
import com.day.cq.wcm.api.WCMMode;

public class ComponentBeanTag extends AbstractComponentTag {

	@Override
	public void doTag() throws JspException, IOException {
		IComponent component = null;
		Resource resource = this.getResource();

		try {
			component = getComponentManager()
					.getComponentFromResource(resource);
		} catch (MandatoryComponentException e) {
			this.getJspContext()
					.getOut()
					.println(
							"Could not render resource at "
									+ this.getResourcePath()
									+ " because one of its mandatory components is not active:"
									+ e);
			this.logger.error(
					"Could not find component for resource "
							+ this.getResourcePath(), e);

			return;
		}

		if (recursionDetected(component)) {
			this.getJspContext().getOut().println(getRecursionStack(component));
		} else {
			this.getJspContext().setAttribute("c", component);
			renderComponent(component);
			this.removeRenderedComponent(component);
		}

	}

	@SuppressWarnings("unchecked")
	private String getRecursionStack(IComponent component) {

		String result = "<pre class=\"pre-scrollable\"><p>Recursion detected in component:"
				+ "<div class=\"text-center alert alert-danger"
				+ "\" style=\"margin:bottom:0px\">"
				+ component
				+ "</div>"
				+ "Aborting rendering process.<h4>Recursion stack:</h4>";
		Iterator<IComponent> components = ((LinkedList<IComponent>) this
				.getSlingHttpServletRequest().getAttribute("CQC:LLC"))
				.iterator();

		while (components.hasNext()) {
			IComponent currentComponent = (IComponent) components.next();
			String alert = "success";
			if (currentComponent == component) {
				alert = "danger";

			}
			result += "<div class=\"text-center alert alert-" + alert
					+ "\" style=\"margin-bottom:0px\">" + currentComponent
					+ "</div>";

			if (components.hasNext()) {
				result += "<div class=\"text-center\"><span class=\"glyphicon glyphicon-chevron-down\"></span></div>";
			}
		}

		result += "</pre>";

		return result;
	}

	@SuppressWarnings("unchecked")
	private void removeRenderedComponent(IComponent component) {
		LinkedList<IComponent> components = (LinkedList<IComponent>) this
				.getSlingHttpServletRequest().getAttribute("CQC:LLC");

		if (components != null) {
			components.remove(component);
		}
	}

	@SuppressWarnings("unchecked")
	private boolean recursionDetected(IComponent component) {
		LinkedList<IComponent> components = (LinkedList<IComponent>) this
				.getSlingHttpServletRequest().getAttribute("CQC:LLC");
		if (components == null) {
			components = new LinkedList<IComponent>();
			this.getSlingHttpServletRequest().setAttribute("CQC:LLC",
					components);
		}

		if (components.contains(component)) {
			components.add(component);
			return true;
		} else {
			components.add(component);
			return false;
		}
	}

	@SuppressWarnings("unchecked")
	private List<ComponentStatistics> getComponentStatistics() {
		List<ComponentStatistics> result = (List<ComponentStatistics>) this
				.getSlingHttpServletRequest().getAttribute(
						"componentsStatistics");

		if (result == null) {
			result = new ArrayList<ComponentStatistics>();
			this.getSlingHttpServletRequest().setAttribute(
					"componentsStatistics", result);
		}
		return result;
	}

	private void renderComponent(IComponent component) throws JspException,
			IOException {
		component.beforeComponentRender();
		String content = null;
		boolean isCachingDisabled = ComponentUtils.isCachingDisabled(
				getResource(), component);
		WCMMode wcmMode = WCMMode
				.fromRequest(this.getSlingHttpServletRequest());

		if (!isCachingDisabled) {
			// Caching is not disabled --> Check for cached content
			content = componentManager.getCachedContent(wcmMode,
					getResourcePath());
		}

		boolean fromCache = true;

		if (content == null) {
			fromCache = false;
			// Content is not available --> Generate it
			StringWriter stringWriter = new StringWriter();
			// Invoke to render html
			this.getJspBody().invoke(stringWriter);
			content = stringWriter.toString();

			if (!isCachingDisabled) {
				// Add it to cache if caching is not disabled
				componentManager
						.addToCache(wcmMode, getResourcePath(), content);
			}
		}
		component.afterComponentRender();

		this.getJspContext().getOut().println(content);
		getComponentStatistics().add(
				new ComponentStatistics(getResourcePath(), component
						.getRenderTime(), fromCache));

	}
}